package com.zb.service.imp.middleware;

import com.alibaba.fastjson.JSON;
import com.zb.entity.exception.BizException;
import com.zb.service.imp.middleware.instruction.Ev3c2Instructions;
import com.zb.service.imp.middleware.instruction.Ev3cInstructions;
import com.zb.service.imp.middleware.instruction.InstructionType;
import com.zb.service.imp.middleware.vo.MessageField;
import com.zb.service.imp.middleware.vo.ProtocolVersion;
import com.zb.service.imp.middleware.vo.UnSendingMessage;
import com.zb.tcp.socket.exception.DuplicateInstructionException;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.*;

/**
 * Created by luoyp on 2016/5/19.
 * 报文解析帮助类, 可以将报文解析成普通的pojo
 */
public class MessageAnalysisHelper
{
	private static Log log = LogFactory.getLog(MessageAnalysisHelper.class);

	/**
	 * 报文解析成普通的pojo
	 * @param msg 报文字符串
	 * @param clz 目标pojo的class类型, 该pojo class需要由@MessageField注释其私有字段
	 * @return 解析后pojo
	 * @throws BizException
	 */
	public static <T> T analyse(String msg, Class<T> clz) throws BizException
	{
		try
		{
			T instance = clz.newInstance();
			List<Field> fields = getAllDeclaredFields(clz);

			List<Field> list_fields = new ArrayList<>();
			for (Field field : fields)
			{
				MessageField c = field.getAnnotation(MessageField.class);
				if (c != null)
					list_fields.add(field);
			}

			Collections.sort(list_fields, new Comparator<Field>()
			{
				@Override
				public int compare(Field f1, Field f2)
				{
					MessageField o1 = f1.getAnnotation(MessageField.class);
					MessageField o2 = f2.getAnnotation(MessageField.class);
					return o1.order() - o2.order();
				}
			});//找出所有被@MessageField注释过的字段, 包括父类的, 并根据order值进行排序

			boolean hasSegment = false;
			int segmentAmount = 0;
			int segmentIndex = 0;
			for (int i = 0, start_index = 0, end_index = 0; i < list_fields.size(); i++)
			{
				Field field = list_fields.get(i);
				MessageField c = field.getAnnotation(MessageField.class);
				int len = c.length() * 2;
				end_index = start_index + len;
				if (c.nullable())
				{
					if (msg.length() < end_index)
						return instance;
				}

				String value = msg.substring(start_index, end_index);
				PropertyUtils.setSimpleProperty(instance, field.getName(), value);
				start_index = end_index;
				if (c.segmentIndicator())
				{
					hasSegment = true;
					segmentAmount = Integer.valueOf(value, 16);
					segmentIndex = start_index;
					break;
				}
			}

			if (hasSegment)//单独处理可变长度字段
			{
				HashMap<String, List<String>> map_values = new HashMap<>();
				for (Field field : list_fields)
				{
					MessageField c = field.getAnnotation(MessageField.class);
					if (c.segmentField())
					{
						ArrayList<String> values = new ArrayList<>();
						map_values.put(field.getName(), values);
					}
				}

				for (int i = 0, start_index = segmentIndex, end_index = segmentIndex; i < segmentAmount; i++)
				{
					for (Field field : list_fields)
					{
						MessageField c = field.getAnnotation(MessageField.class);
						if (c.segmentField())
						{
							int len = c.length() * 2;
							end_index = start_index + len;
							String value = msg.substring(start_index, end_index);
							map_values.get(field.getName()).add(value);
							//							System.out.println(field.getName() + i);
							if (i == segmentAmount - 1)
								PropertyUtils.setSimpleProperty(instance, field.getName(), map_values.get(field.getName()));
							start_index = end_index;
						}
					}
				}
			}

			return instance;
		}
		catch (InvocationTargetException | NoSuchMethodException | InstantiationException | IllegalAccessException e)
		{
			log.error(e.getMessage(), e);
			throw new BizException(e);
		}
	}

	/**
	 * 查找所有父类中的私有成员
	 * @param startClass 子类类型
	 * @return 父类中所有私有成员数组
	 */
	private static List<Field> getAllDeclaredFields(Class<?> startClass)
	{
		List<Field> currentClassFields = new ArrayList<>(Arrays.asList(startClass.getDeclaredFields()));
		Class<?> parentClass = startClass.getSuperclass();

		if (parentClass != null)
		{
			List<Field> parentClassFields = getAllDeclaredFields(parentClass);
			currentClassFields.addAll(parentClassFields);
		}
		return currentClassFields;
	}

	/**
	 * 将报文解析成一个UnSendingMessage类的实例
	 * @param message 报文
	 * @param chargerId 桩号
	 * @return 解析好的一个UnSendingMessage类的实例
	 */
	public static UnSendingMessage analyseSendingMessage(String message, String chargerId)
	{
		if (message == null)
			throw new NullPointerException("message is null");
		if (!message.startsWith("data="))
			throw new IllegalArgumentException("invalid message which do not start with 'data='");

		String msg = message.substring(9);

		if (!msg.startsWith("68"))
			throw new IllegalArgumentException("invalid message which do not start with 68");

		UnSendingMessage s = new UnSendingMessage();
		s.setMessage(msg);
		s.setChargerId(chargerId);

		int pos = msg.indexOf(chargerId);
		if (pos == 12)
		{
			s.setProtocolVersion(ProtocolVersion.Version_2);
			s.setCommand(msg.substring(6, 8));
		}
		else if (pos == 18)
		{
			s.setProtocolVersion(ProtocolVersion.Version_1);
			s.setCommand(msg.substring(34, 36));
		}
		else
			throw new IllegalArgumentException("invalid message with wrong charger id position");

		return s;
	}

	/**
	 * 取服务器下发报文中的流水号
	 * @param message 报文
	 * @param chargerId 桩号
	 * @return 流水号
	 */
	public static String getSerialNoFromSendingMessage(String message, String chargerId)
	{
		int pos = message.indexOf(chargerId);
		String serialNo = null;
		if (pos == 12)
		{
			Ev3c2Instructions instruction = Ev3c2Instructions.parse(message.substring(6, 8));
			if(instruction.type() == InstructionType.SendByServer)
			{
				serialNo = message.substring(32, 50);
			}
		}
		else if (pos == 18)
		{
			Ev3cInstructions instruction = Ev3cInstructions.parse( message.substring(34, 36));
			if(instruction.type() == InstructionType.SendByServer)
			{
				serialNo = message.substring(36, 50);
			}
		}
		else
			throw new IllegalArgumentException("invalid message with wrong charger id position");

		return serialNo;
	}

}
